from abc import ABCMeta, abstractmethod
from hashlib import md5
from threading import Lock
import requests
import time
import json
from requests.exceptions import *
from v5.Page import Page
from v5.Exception import SpiderBaseException
import sys


class Filter(object):
    m5 = md5()
    result_md5_set = set()

    def __init__(self):
        self.lock = Lock()

    def filter(self, response):
        """
        filter对结果进行过滤去重, 利用数据算出md5值, 如果已经存在相同数据, 则不需要将请求结果加入回应队列
        :param response: 请求结果 requests.Response
        :return: None
        """
        try:
            self.lock.acquire()
            self.m5.update(response.content)
            hexdigest = self.m5.hexdigest()
            if hexdigest not in self.result_md5_set:
                self.result_md5_set.add(hexdigest)
                return response
            else:
                return None
        finally:
            self.lock.release()


class Downloader(object):
    __metaclass__ = ABCMeta

    @abstractmethod
    def download(self, request_item, sock_puppet):
        pass


class DefaultDownloader(Downloader):
    def __init__(self):
        self.filter = Filter()

    def process(self, request_item, headers):
        item = request_item.get()
        method = item['method'].upper()
        url = item['url']
        timeout = item['timeout']
        params = item['params']
        request_body = item['request_body']
        sleep_time = int(item['sleep_time'])
        req = requests.session()

        try:
            if method == 'GET':
                res = req.get(
                    url=url,
                    headers=headers,
                    timeout=timeout,
                    params=params
                )
                time.sleep(sleep_time)
                return res
            elif method == 'POST':
                res = req.post(
                    url=url,
                    headers=headers,
                    timeout=timeout,
                    data=json.dumps(request_body)
                )
                time.sleep(sleep_time)
                return res
        except Exception as e:
            raise e

    def download(self, page, sock_puppet):
        headers, i = sock_puppet.get_headers()
        request_item = page.get_request()
        try:
            res = self.process(request_item, headers)
            page.set_download_result(res)
        except SSLError as e:
            pass
        except ConnectTimeout as e:
            pass
        except Exception as e:
            raise e